Skip to content

Fix #11168 FP returnDanglingLifetime (local iterator points at global struct) - #8979

Open
chrchr-github wants to merge 2 commits into
cppcheck-opensource:mainfrom
chrchr-github:chr_11168
Open

chrchr-github wants to merge 2 commits into
cppcheck-opensource:mainfrom
chrchr-github:chr_11168

Conversation

@chrchr-github

Copy link
Copy Markdown
Collaborator

No description provided.

@dmcppcheck dmcppcheck left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

This PR causes false negatives when a member is an array. The new condition stops following the lifetime at . whenever the top token is a unary * and the right side of . has a varId. It never checks that the member is a pointer, so it also applies to array members. In that case *s.a is part of the local s. Real dangling returns such as return &*s.a;, return *s.a; (returning int&) and return &*s.b[0]; are now no longer reported; base cppcheck reports all three. The FP from #11168 (&*x.a where a is a pointer) is fixed.

A safer check would test that the member really is a pointer and not an array, for example vt->pointer > 0 together with !variable()->isArray(). Tests for the array case should be added to keep this behaviour from breaking again.

Testing

I built cppcheck and testrunner for both pr/ and base/. ./testrunner TestAutoVariables passes, and so does the full ./testrunner (5374 tests, 0 failures).

Test file scratch/a.cpp:

struct S { int a[10]; int b[3][3]; int* p; };
int* f1() { S s; return &*s.a; }
int* f2() { S s{}; return &*s.b[0]; }
int& f3() { S s{}; return *s.a; }
int* f4() { S s{}; return &s.a[0]; }
int* g1() { S s; return &*s.p; }

Command: cppcheck -q --enable=all a.cpp

line base pr
f1 &*s.a (array) returnDanglingLifetime (correct) nothing (FN)
f2 &*s.b[0] (2D array) returnDanglingLifetime (correct) nothing (FN)
f3 *s.a returned as int& returnReference (correct) nothing (FN)
f4 &s.a[0] returnDanglingLifetime returnDanglingLifetime
g1 &*s.p (pointer) returnDanglingLifetime (FP) nothing (fixed)

Iterator members (return *t.it; / &*t.it) are no longer reported either. That looks correct, because the iterator may point into some other container.


Automated review of cf0a3b9 by Claude. The pull request was built and tested locally.

Comment thread lib/valueflow.cpp
else if (vartok->str() == ".") {
if (!Token::simpleMatch(vartok->astOperand1(), ".") &&
!(vartok->astOperand2() && vartok->astOperand2()->valueType() && vartok->astOperand2()->valueType()->reference != Reference::None))
!(vartok->astOperand2() && ((vartok->astOperand2()->varId() && tok->isUnaryOp("*")) || (vartok->astOperand2()->valueType() && vartok->astOperand2()->valueType()->reference != Reference::None))))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

varId() && tok->isUnaryOp("*") is true for array members too, and there dereferencing gives an element of the local object. Example: struct S { int a[10]; }; int* f() { S s; return &*s.a; } and int& f() { S s{}; return *s.a; }. Both were reported before (returnDanglingLifetime / returnReference) and are silent with this PR. The same happens for &*s.b[0] with int b[3][3]. Consider checking that the member is a real pointer, e.g. vt && vt->pointer > 0 && !(var && var->isArray()), and add tests for the array cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants